{ "cells": [ { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "# Monte Carlo Simulation and Linear Algebra" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "-" }, "tags": [ "remove-cell" ] }, "source": [ "**CS1302 Introduction to Computer Programming**\n", "___" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "ExecuteTime": { "end_time": "2020-11-27T11:20:04.656873Z", "start_time": "2020-11-27T11:20:04.651575Z" }, "slideshow": { "slide_type": "fragment" }, "tags": [ "remove-cell" ] }, "outputs": [], "source": [ "import math\n", "import random\n", "import ipywidgets as widgets\n", "%reload_ext mytutor" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "## Monte Carlo simulation" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "**What is Monte Carlo simulation?**" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ " > The name Monte Carlo refers to the [Monte Carlo Casino in Monaco](https://en.wikipedia.org/wiki/Monte_Carlo_Casino) where Ulam's uncle would borrow money from relatives to gamble." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "It would be nice to simulate the casino, so Ulam's uncle did not need to borrow money to play in the casino. \n", "Actually...,\n", "- Monte Carlo is the code name of the secret project for creating the [hydrogen bomb](https://en.wikipedia.org/wiki/Monte_Carlo_method). \n", "- [Ulam](https://en.wikipedia.org/wiki/Stanislaw_Ulam) worked with [John von Neumann](https://en.wikipedia.org/wiki/John_von_Neumann) to program the first electronic computer ENIAC to simulate a computational model of a thermonuclear reaction.\n", "\n", "(See also [The Beginning of the Monte Carlo Method](https://permalink.lanl.gov/object/tr?what=info:lanl-repo/lareport/LA-UR-88-9067) for a more detailed account.)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "**How to compute the value of $\\pi$**?" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "An application of Monte Carlo simulation is in approximating $\\pi$ using \n", "the [Buffon's needle](https://en.wikipedia.org/wiki/Buffon%27s_needle_problem). \n", "There is [a program](https://www.khanacademy.org/computer-programming/pi-by-buffons-needle/6695500989890560) written in javascript to do this." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "The javascript program is a bit long to digest, so we will use an alternative simulation that is easier to understand/program." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "If we uniformly randomly pick a point in a square. What is the chance it is in the inscribed circle, i.e., the biggest circle inside the square?" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "-" } }, "source": [ "The chance is the area of the circle divided by the area of the square. Suppose the square has length $\\ell$, then the chance is\n", "\n", "$$ \\frac{\\pi (\\ell /2)^2}{ (\\ell)^2 } = \\frac{\\pi}4 $$\n", "independent of the length $\\ell$." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "**Exercise** Complete the following function to return an approximation of $\\pi$ as follows:\n", "1. Simulate the random process of picking a point from a square repeatedly `n` times by \n", " generating the $x$ and $y$ coordinates uniformly randomly from a unit interval $[0,1)$.\n", "2. Compute the fraction of times the point is in the first quadrant of the inscribed circle as shown in the figure below.\n", "3. Return $4$ times the fraction as the approximation.\n", "
" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "ExecuteTime": { "end_time": "2020-11-17T05:05:07.672010Z", "start_time": "2020-11-17T05:05:04.428686Z" }, "nbgrader": { "grade": false, "grade_id": "approximate_pi", "locked": false, "schema_version": 3, "solution": true, "task": false }, "slideshow": { "slide_type": "-" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Approximate: 3.1424256\n", "Ground truth: 3.141592653589793\n" ] } ], "source": [ "def approximate_pi(n):\n", " ### BEGIN SOLUTION\n", " return (\n", " 4\n", " * len(\n", " [True for i in range(n) if random.random() ** 2 + random.random() ** 2 < 1]\n", " )\n", " / n\n", " )\n", " ### END SOLUTION\n", "print(f\"Approximate: {approximate_pi(int(1e7))}\\nGround truth: {math.pi}\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "**How accurate is the approximation?**" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "The following uses a powerful library `numpy` for computing to return a [$95\\%$-confidence interval](http://onlinestatbook.com/2/estimation/mean.html#:~:text=To%20compute%20the%2095%25%20confidence,be%20between%20the%20cutoff%20points.)." ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "ExecuteTime": { "end_time": "2020-11-17T05:05:08.218178Z", "start_time": "2020-11-17T05:05:07.673752Z" }, "slideshow": { "slide_type": "-" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "95%-confidence interval: [3.14129292 3.14336948]\n", "Estimate: 3.1423 ± 0.0010\n", "Ground truth: 3.141592653589793\n" ] } ], "source": [ "import numpy as np\n", "\n", "\n", "def np_approximate_pi(n):\n", " in_circle = (np.random.random((n, 2)) ** 2).sum(axis=-1) < 1\n", " mean = 4 * in_circle.mean()\n", " std = 4 * in_circle.std() / n ** 0.5\n", " return np.array([mean - 2 * std, mean + 2 * std])\n", "\n", "\n", "interval = np_approximate_pi(int(1e7))\n", "print(\n", " f\"\"\"95%-confidence interval: {interval}\n", "Estimate: {interval.mean():.4f} ± {(interval[1]-interval[0])/2:.4f}\n", "Ground truth: {math.pi}\"\"\"\n", ")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Note that the computation done using `numpy` is over $5$ times faster despite the additional computation of the standard deviation." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "There are faster methods to approximate $\\pi$ such as the [Chudnovsky_algorithm](https://en.wikipedia.org/wiki/Chudnovsky_algorithm), but Monte-Carlo method is still useful in more complicated situations. \n", "E.g., see the Monte Carlo simulation of a [real-life situation](https://www.youtube.com/watch?v=-fCVxTTAtFQ) in playing basketball:\n", "> \"When down by three and left with only 30 seconds is it better to attempt a hard 3-point shot or an easy 2-point shot and get another possession?\" --LeBron James" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Linear Algebra" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "**How to solve a linear equation?**" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Given the following linear equation in variable $x$ with real-valued coefficient $a$ and $b$,\n", "\n", "$$ a x = b,$$\n", "what is the value of $x$ that satisfies the equation?" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "**Exercise** Complete the following function to return either the unique solution of $x$ or `None` if a unique solution does not exist." ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "ExecuteTime": { "end_time": "2020-11-17T05:05:08.250889Z", "start_time": "2020-11-17T05:05:08.220095Z" }, "nbgrader": { "grade": false, "grade_id": "solve_linear_equation", "locked": false, "schema_version": 3, "solution": true, "task": false }, "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "706ee6de5f904f17a4dfacd90c0d4d22", "version_major": 2, "version_minor": 0 }, "text/plain": [ "interactive(children=(IntSlider(value=2, description='a', max=5), IntSlider(value=3, description='b', max=5), …" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "def solve_linear_equation(a, b):\n", " ### BEGIN SOLUTION\n", " return b / a if a != 0 else None\n", " ### END SOLUTION\n", "\n", "\n", "@widgets.interact(a=(0, 5, 1), b=(0, 5, 1))\n", "def linear_equation_solver(a=2, b=3):\n", " print(\n", " f\"\"\"linear equation: {a}x = {b}\n", " solution: x = {solve_linear_equation(a,b)}\"\"\"\n", " )" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "**How to solve multiple linear equations?**" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "In the general case, we have a system of $m$ linear equations and $n$ variables:\n", "\n", "$$ \\begin{aligned}\n", "a_{00} x_0 + a_{01} x_1 + \\dots + a_{0(n-1)} x_{n-1} &= b_0\\\\\n", "a_{10} x_0 + a_{11} x_1 + \\dots + a_{1(n-1)} x_{n-1} &= b_1\\\\\n", "\\vdots\\kern2em &= \\vdots\\\\\n", "a_{(m-1)0} x_0 + a_{(m-1)1} x_1 + \\dots + a_{(m-1)(n-1)} x_{n-1} &= b_{m-1}\\\\\n", "\\end{aligned}\n", "$$\n", "where\n", "- $x_j$ for $j\\in \\{0,\\dots,n-1\\}$ are the variables, and\n", "- $a_{ij}$ and $b_j$ for $i\\in \\{0,\\dots,m-1\\}$ and $j\\in \\{0,\\dots,n-1\\}$ are the coefficients.\n", "\n", "A fundamental problem in linear algebra is to compute the unique solution to the system if it exists." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "We will consider the simpler 2-by-2 system with 2 variables and 2 equations:\n", "\n", "$$ \\begin{aligned}\n", "a_{00} x_0 + a_{01} x_1 &= b_0\\\\\n", "a_{10} x_0 + a_{11} x_1 &= b_1.\n", "\\end{aligned}\n", "$$" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "To get an idea of the solution, suppose \n", "\n", "$$a_{00}=a_{11}=1, a_{01} = a_{10} = 0.$$\n", "The system of equations becomes\n", "\n", "$$ \\begin{aligned}\n", "x_0 \\hphantom{+ x_1} &= b_0\\\\\n", "\\hphantom{x_0 +} x_1 &= b_1,\n", "\\end{aligned}\n", "$$\n", "which gives the solution directly." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "What about $a_{00}=a_{11}=2$ instead?\n", "\n", "$$ \\begin{aligned}\n", "2x_0 \\hphantom{+ x_1} &= b_0\\\\\n", "\\hphantom{2x_0 +} 2x_1 &= b_1,\n", "\\end{aligned}$$" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "To obtain the solution, we simply divide both equations by 2:\n", "\n", "$$ \\begin{aligned}\n", "x_0 \\hphantom{+ x_1} &= \\frac{b_0}2\\\\\n", "\\hphantom{x_0 +} x_1 &= \\frac{b_1}2.\n", "\\end{aligned}\n", "$$" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "What if $a_{01}=2$ instead?\n", "\n", "$$ \\begin{aligned}\n", "2x_0 + 2x_1 &= b_0\\\\\n", "\\hphantom{2x_0 +} 2x_1 &= b_1\\\\\n", "\\end{aligned}\n", "$$" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "The second equation gives the solution of $x_1$, and we can use the solution in the first equation to solve for $x_0$. More precisely:\n", "- Subtract the second equation from the first one:\n", "\n", "$$ \\begin{aligned}\n", "2x_0 \\hphantom{+2x_1} &= b_0 - b_1\\\\\n", "\\hphantom{2x_0 +} 2x_1 &= b_1\\\\\n", "\\end{aligned}\n", "$$\n", "- Divide both equation by 2:\n", "\n", "$$ \\begin{aligned}\n", "x_0 \\hphantom{+ x_1} &= \\frac{b_0 - b_1}2\\\\\n", "\\hphantom{x_0 +} x_1 &= \\frac{b_1}2\\\\\n", "\\end{aligned}\n", "$$" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "The above operations are called *row operations* in linear algebra: each row is an equation. \n", "A system of linear equations can be solved by the linear operations of \n", "1. multiplying an equation by a constant, and\n", "2. subtracting one equation from another." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "How to write a program to solve a general 2-by-2 system? We will use the `numpy` library." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### Creating `numpy` arrays" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "**How to store the coefficients?**" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "In linear algebra, a system of equations such as\n", "\n", "$$ \\begin{aligned}\n", "a_{00} x_0 + a_{01} x_1 &= b_0\\\\\n", "a_{10} x_0 + a_{11} x_1 &= b_1\n", "\\end{aligned}\n", "$$\n", "is written concisely in *matrix* form as $ \\mathbf{A} \\mathbf{x} = \\mathbf{b} $:\n", "\n", "$$\\overbrace{\\begin{bmatrix}\n", "a_{00} & a_{01}\\\\\n", "a_{10} & a_{11}\n", "\\end{bmatrix}}^{\\mathbf{A}}\n", "\\overbrace{\n", "\\begin{bmatrix}\n", "x_0\\\\\n", "x_1\n", "\\end{bmatrix}}\n", "^{\\mathbf{x}}\n", "= \\overbrace{\\begin{bmatrix}\n", "b_0\\\\\n", "b_1\n", "\\end{bmatrix}}^{\\mathbf{b}},\n", "$$\n", "where\n", "$ \\mathbf{A} \\mathbf{x}$ is the *matrix multiplication*\n", "\n", "$$ \\mathbf{A} \\mathbf{x} = \\begin{bmatrix}\n", "a_{00} x_0 + a_{01} x_1\\\\\n", "a_{10} x_0 + a_{11} x_1\n", "\\end{bmatrix}.\n", "$$" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "We say that $\\mathbf{A}$ is a [*matrix*](https://en.wikipedia.org/wiki/Matrix_(mathematics)) and its dimension/shape is $2$-by-$2$:\n", "- The first dimension/axis has size $2$. We also say that the matrix has $2$ rows.\n", "- The second dimension/axis has size $2$. We also say that the matrix has $2$ columns.\n", "$\\mathbf{x}$ and $\\mathbf{b}$ are called column vectors, which are matrices with one column." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Consider the example\n", "\n", "$$ \\begin{aligned}\n", "2x_0 + 2x_1 &= 1\\\\\n", "\\hphantom{2x_0 +} 2x_1 &= 1,\n", "\\end{aligned}$$\n", "\n", "or in matrix form with\n", "\n", "$$ \\begin{aligned}\n", "\\mathbf{A}&=\\begin{bmatrix}\n", "a_{00} & a_{01} \\\\\n", "a_{10} & a_{11} \n", "\\end{bmatrix} \n", "= \\begin{bmatrix}\n", "2 & 2 \\\\\n", "0 & 2 \n", "\\end{bmatrix}\\\\\n", "\\mathbf{b}&=\\begin{bmatrix}\n", "b_0\\\\\n", "b_1\n", "\\end{bmatrix} = \\begin{bmatrix}\n", "1\\\\\n", "1\n", "\\end{bmatrix}\\end{aligned}$$" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Instead of using `list` to store the matrix, we will use a `numpy` array." ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "ExecuteTime": { "end_time": "2020-11-17T05:05:08.258666Z", "start_time": "2020-11-17T05:05:08.252256Z" }, "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "(array([[2., 2.],\n", " [0., 2.]]),\n", " array([1., 1.]))" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A = np.array([[2.0, 2], [0, 2]])\n", "b = np.array([1.0, 1])\n", "A, b" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Compared to `list`, `numpy` array is often more efficient and has more useful attributes." ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "ExecuteTime": { "end_time": "2020-11-17T05:05:08.277037Z", "start_time": "2020-11-17T05:05:08.259940Z" }, "slideshow": { "slide_type": "-" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "Common attributes:\n", " copy sort\n", "\n", "Array-specific attributes:\n", " dtype max reshape dot compress getfield argmax view conjugate strides swapaxes trace squeeze put cumprod imag nbytes nonzero fill real size item shape T round tolist cumsum itemset resize var std take tostring repeat choose base flat clip flags diagonal argmin dumps setfield partition ptp mean searchsorted sum data ravel transpose conj ctypes argsort setflags dump itemsize astype any tobytes ndim min newbyteorder prod all tofile flatten byteswap argpartition\n", "\n", "List-specific attributes:\n", " count remove clear index insert reverse append pop extend\n" ] } ], "source": [ "array_attributes = set(attr for attr in dir(np.array([])) if attr[0] != \"_\")\n", "list_attributes = set(attr for attr in dir(list) if attr[0] != \"_\")\n", "print(\"\\nCommon attributes:\\n\", *(array_attributes & list_attributes))\n", "print(\"\\nArray-specific attributes:\\n\", *(array_attributes - list_attributes))\n", "print(\"\\nList-specific attributes:\\n\", *(list_attributes - array_attributes))" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "The following attributes give the dimension/shape, number of dimensions, size, and data type." ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "ExecuteTime": { "end_time": "2020-11-17T05:05:08.284772Z", "start_time": "2020-11-17T05:05:08.278614Z" }, "slideshow": { "slide_type": "-" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[2. 2.]\n", " [0. 2.]]\n", " shape: (2, 2)\n", " ndim: 2\n", " size: 4\n", " dtype: float64\n", " \n", "[1. 1.]\n", " shape: (2,)\n", " ndim: 1\n", " size: 2\n", " dtype: float64\n", " \n" ] } ], "source": [ "for array in A, b:\n", " print(\n", " f\"\"\"{array}\n", " shape: {array.shape}\n", " ndim: {array.ndim}\n", " size: {array.size}\n", " dtype: {array.dtype}\n", " \"\"\"\n", " )" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Note that the function `len` only returns the size of the first dimension:" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "ExecuteTime": { "end_time": "2020-11-17T05:05:08.316025Z", "start_time": "2020-11-17T05:05:08.310303Z" }, "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "2" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "assert A.shape[0] == len(A)\n", "len(A)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Unlike `list`, every `numpy` array has a data type. For efficient computation/storage, numpy implements different data types with different storage sizes:\n", "* integer: `int8`, `int16`, `int32`, `int64`, `uint8`, ...\n", "* float: `float16`, `float32`, `float64`, ...\n", "* complex: `complex64`, `complex128`, ...\n", "* boolean: `bool8`\n", "* Unicode: `string`\n", "* Object: `object`" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "E.g., `int64` is the 64-bit integer. Unlike `int`, `int64` has a range." ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "ExecuteTime": { "end_time": "2020-11-17T05:05:08.580209Z", "start_time": "2020-11-17T05:05:08.528600Z" }, "slideshow": { "slide_type": "-" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "range: -9223372036854775808 to 9223372036854775807\n" ] }, { "ename": "OverflowError", "evalue": "Python int too large to convert to C long", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mOverflowError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m/tmp/ipykernel_861/1402166890.py\u001b[0m in \u001b[0;36m